#include "MFUI.hpp"

#ifdef WIN32

#include<windows.h>

static wstring A2W(const string &str, UINT uCodePage)
{
    int nLength = ::MultiByteToWideChar(uCodePage, 0, str.c_str(), -1, NULL, 0);
    wstring strW(nLength, L'\0');
    int nResult = ::MultiByteToWideChar(uCodePage, 0, str.c_str(), -1, &strW[0], nLength);
    strW.resize(nLength - 1);
    return strW;
}//from Web
static string W2A(const wstring &str, UINT uCodePage)
{
    int nLength = ::WideCharToMultiByte(uCodePage, 0, str.c_str(), -1, NULL, 0, NULL, NULL);
    string strA(nLength, '\0');
    int nResult = ::WideCharToMultiByte(uCodePage, 0, str.c_str(), -1, &strA[0], nLength, NULL, NULL);
    strA.resize(nLength - 1);
    return strA;
}//from web

vector<string*>* GetDirAllFile(const char* path){
	HANDLE hfind;
	WIN32_FIND_DATAA find;
	string cache(path);
	cache.append("/*");
	hfind=FindFirstFileA(cache.c_str(),&find);
	if(hfind==INVALID_HANDLE_VALUE){
		return NULL;
	}
	auto temp=new vector<string*>();
	do{
		if(find.dwFileAttributes==FILE_ATTRIBUTE_DIRECTORY){
			string *st=new string("/");
            st->append(find.cFileName);
			temp->push_back(st);
		}
		else{
			temp->push_back(new string(find.cFileName));
		}
	}while(FindNextFileA(hfind,&find));
	FindClose(hfind);
	return temp;
}

vector<string*>* GetDirFile(const char* path){
	HANDLE hfind;
	WIN32_FIND_DATAA find;
	string cache(path);
	cache.append("/*");
	hfind=FindFirstFileA(cache.c_str(),&find);
	if(hfind==INVALID_HANDLE_VALUE){
		return NULL;
	}
	auto temp=new vector<string*>();
	do{
		if(find.dwFileAttributes==FILE_ATTRIBUTE_DIRECTORY){
			continue;
		}
		else{
			temp->push_back(new string(find.cFileName));
		}
	}while(FindNextFileA(hfind,&find));
	FindClose(hfind);
	return temp;
}

#else

#include<dirent.h>
#include<sys/stat.h>

vector<string*>* GetDirAllFile(const char* path){
	DIR* dir=opendir(path);
	if(!dir){
		return NULL;
	}
	auto temp=new vector<string*>();
	struct dirent *direntp;
	int end=strlen(path);
	char* base;
	if(path[end-1]!='/'){
		base=new char[++end+1]();
		strcat(strcat(base,path),"/");
	}
	else{
		base=new char[++end]();
		strcat(base,path);
	}
	++end;
	while((direntp=readdir(dir))){
		struct stat s_buf;
		char* cur=new char[strlen(direntp->d_name)+end]();
		strcat(cur,base);
		strcat(cur,direntp->d_name);
		stat(cur,&s_buf);
		delete[] cur;
		if(S_ISDIR(s_buf.st_mode)){
			string* st=new string("/");
			st->append(direntp->d_name);
			temp->push_back(st);
		}
		else{
			temp->push_back(new string(direntp->d_name));
		}
	}
	closedir(dir);
	delete[] base;
	return temp;
}

vector<string*>* GetDirFile(const char* path){
	DIR* dir=opendir(path);
	if(!dir){
		return NULL;
	}
	auto temp=new vector<string*>();
	struct dirent *direntp;
	int end=strlen(path);
	char* base;
	if(path[end-1]!='/'){
		base=new char[++end+1]();
		strcat(strcat(base,path),"/");
	}
	else{
		base=new char[++end]();
		strcat(base,path);
	}
	++end;
	while((direntp=readdir(dir))){
		struct stat s_buf;
		char* cur=new char[strlen(direntp->d_name)+end]();
		strcat(cur,base);
		strcat(cur,direntp->d_name);
		stat(cur,&s_buf);
		delete[] cur;
		if(S_ISDIR(s_buf.st_mode)){
			continue;
		}
		else{
			temp->push_back(new string(direntp->d_name));
		}
	}
	closedir(dir);
	delete[] base;
	return temp;
}

#endif

void CloseWindow(unsigned int id){
    auto f=windows->find(id);
    if(f!=windows->end()){
        delete f->second;
    }
}

//font_size 默认加载字体的大小 请确保可执行文件同目录下有siyuan.ttf 否则抛出FileNotFoundException
void MFInit(int font_size){
    SDL_Init(SDL_INIT_EVERYTHING);
    Mix_Init(MIX_INIT_MP3);
    Mix_OpenAudio(48000,AUDIO_S16,2,4096);
    IMG_Init(IMG_INIT_PNG);
    SDL_SetHint(SDL_HINT_IME_INTERNAL_EDITING,"1");
    TTF_Init();
    Mix_Volume(-1,128);
    Mix_VolumeMusic(128);
    default_font=new Font("./siyuan.ttf",font_size);
    windows=new unordered_map<Uint32,Window*>();
    edit_cursor=SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_IBEAM);
    arrow_cursor=SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_ARROW);
    button_cursor=SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_HAND);
}

void MFQuit(){
    delete windows;
    delete default_font;
    Mix_CloseAudio();
    TTF_Quit();
    Mix_Quit();
    IMG_Quit();
    SDL_FreeCursor(edit_cursor);
    SDL_FreeCursor(arrow_cursor);
    SDL_FreeCursor(button_cursor);
    SDL_Quit();
}

int MFMain(Uint32 *delta_receiver){
    SDL_Event event;
    Uint32 timer=0;
    for(;;){
        while(SDL_PollEvent(&event)){
            if(windows->empty()){
                return 0;
            }
            switch(event.type){
                case SDL_WINDOWEVENT:
                {
                    auto id=event.window.windowID;
                    switch(event.window.event){
                        case SDL_WINDOWEVENT_CLOSE:
                        {
                            auto w=windows->find(id)->second;
                            if(!w->_close()){
                                continue;
                            }
                            delete w;
                        }
                        continue;
                        case SDL_WINDOWEVENT_RESIZED:
                        windows->find(id)->second->OnWindowResize(event.window.data1,event.window.data2);
                        continue;
                    }
                    continue;
                }
                case SDL_KEYUP:
                windows->find(event.key.windowID)->second->CheckKeyUp(event.key.keysym);
                continue;
                case SDL_KEYDOWN:
                windows->find(event.key.windowID)->second->CheckKeyDown(event.key.keysym);
                continue;
                case SDL_TEXTINPUT:
                windows->find(event.text.windowID)->second->CheckTextInput(event.text.text);
                continue;
                case SDL_MOUSEWHEEL:
                windows->find(event.wheel.windowID)->second->CheckMouseWheel(event.wheel);
                continue;
                case SDL_MOUSEBUTTONDOWN:
                windows->find(event.button.windowID)->second->CheckMouseDown(event.button.x,event.button.y,
                event.button.clicks,event.button.button);
                continue;
                case SDL_MOUSEBUTTONUP:
                windows->find(event.button.windowID)->second->CheckMouseUp(event.button.x,event.button.y,
                event.button.clicks,event.button.button);
                continue;
                case SDL_MOUSEMOTION:
                windows->find(event.motion.windowID)->second->CheckMouseMove(event.motion.x,event.motion.y);
                continue;
            }
        }
        for(auto item=windows->begin(),end=windows->end();item!=end;item++){
            item->second->Draw();
        }
        Uint32 t=SDL_GetTicks();
        *delta_receiver=t-timer;
        if(*delta_receiver<frame_delay){
            SDL_Delay(frame_delay-*delta_receiver);
        }
        timer=t;
    }
}

MFPack::MFDirectory::~MFDirectory(){
    for(auto& n:file)delete n.second;
}

MFPack::MFPack(const char* path){
    FILE* fp=fopen(path,"rb");
    if(!fp){
        printf("pack %s not exist\n",path);
        return;
    }
    char magic[4];
    fread(magic,4,1,fp);
    if(strncmp(magic,"MFPK",4)){
        printf("pack %s invalid\n",path);
        return;
    }
    size_t ndir=0;
    fread(&ndir,4,1,fp);
    while(ndir--){
        unsigned int l=0;
        unsigned int n=0;//内容个数
        fread(&l,1,1,fp);
        fread(&n,4,1,fp);
        auto dir=new char[l+1]();
        fread(dir,l,1,fp);
        auto o=new MFPack::MFDirectory();
        this->dir.emplace(dir,o);
        while(n--){
            l=0;
            fread(&l,1,1,fp);
            auto file=new char[l+1]();
            fread(file,l,1,fp);
            unsigned long long content;
            fread(&content,8,1,fp);
            auto buff=new string();
            buff->resize(content);
            fread((void*)buff->data(),content,1,fp);
            o->file.emplace(file,buff);
            delete[] file;
        }
        delete[] dir;
    }
    fclose(fp);
}
MFPack::~MFPack(){
    for(auto& n:dir)delete n.second;
}
MFPack::MFDirectory* MFPack::GetDir(const char* name){
    auto f=dir.find(name);
    if(f!=dir.end())return f->second;
    return 0;
}
void MFPack::Pack(const char* pack_path,initializer_list<const char*> dirs){
    FILE* fp=fopen(pack_path,"wb");
    auto n=dirs.size();
    fwrite("MFPK",4,1,fp);
    fwrite(&n,4,1,fp);
    for(auto& n:dirs){
        auto l=GetDirFile(n);
        unsigned long long len=strlen(n);
        unsigned long long lsize=l->size();
        fwrite(&len,1,1,fp);
        fwrite(&lsize,4,1,fp);
        fwrite(n,len,1,fp);
        string d(n);
        for(auto& s:*l){
            len=s->size();
            fwrite(&len,1,1,fp);
            fwrite(s->c_str(),len,1,fp);
            FILE* temp=fopen((d+*s).c_str(),"rb");
            fseek(temp,0,SEEK_END);
            len=ftell(temp);
            fseek(temp,0,SEEK_SET);
            fwrite(&len,8,1,fp);
            auto buff=new char[len];
            fread(buff,len,1,temp);
            fwrite(buff,len,1,fp);
            fclose(temp);
            delete s;
            delete[] buff;
        }
        delete l;
    }
    fclose(fp);
}